<template>
    <div>
        <input type="file" @change="handleFileChange"/> <!-- 限制只能选择视频文件 -->
        <button @click="upload">开始上传</button>
        <progress :value="progress" max="100"></progress>
    </div>
</template>
  
<script lang="ts">
import { ref, defineComponent } from 'vue';
import axios from 'axios';
import CryptoJS from 'crypto-js';

export default defineComponent({
    name: 'LargeFileUpload',
    setup() {
        const file = ref<File | null>(null); // 存储选中的文件
        const progress = ref<number>(0); // 上传进度
        const uploadedChunks = ref<string[]>([]); // 已上传的分片索引

        // 当文件选择改变时触发
        const handleFileChange = (event: Event) => {
            const target = event.target as HTMLInputElement;// 获取文件对象
            if (target.files && target.files.length > 0) {
                file.value = target.files[0];
                // 清除已上传的分片记录
                uploadedChunks.value = [];
            }
        };

        // 分片函数，将文件切分为多个小块
        const createFileChunk = (file: File, size = 1 * 1024 * 1024): File[] => {// 1MB
            const chunks: File[] = [];// 存储分片后的文件
            let cur = 0;
            while (cur < file.size) {
                chunks.push(file.slice(cur, cur + size));// 将文件切分为多个小块
                cur += size;
            }
            return chunks;
        };

        // 计算文件的校验和
        const calculateChecksum = (file: File): Promise<string> => {// 计算文件的校验和
            return new Promise((resolve, reject) => {// 创建一个Promise对象
                const fileReader = new FileReader();// 创建一个FileReader对象
                fileReader.onload = (event) => {// 当文件读取完成时触发
                    const arrayBuffer = event.target?.result as ArrayBuffer;// 获取文件的ArrayBuffer
                    const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(arrayBuffer));// 将ArrayBuffer转换为WordArray
                    const hash = CryptoJS.MD5(wordArray).toString();// 计算文件的MD5值
                    resolve(hash);
                };
                fileReader.onerror = (error) => {// 当文件读取出错时触发
                    reject(error);// 返回错误信息
                };
                fileReader.readAsArrayBuffer(file);// 将文件读取为ArrayBuffer
            });
        };



        // 检查分片是否已上传
        const isChunkUploaded = (index: number): boolean => {
            return uploadedChunks.value.includes(index.toString());// 检查分片是否已上传
        };

        // 记录已上传的分片
        const markChunkAsUploaded = (index: number) => {
            if (!uploadedChunks.value.includes(index.toString())) {
                uploadedChunks.value.push(index.toString());// 将分片索引添加到已上传的分片列表中
            }
        };

        // 开始上传
        const upload = async () => {
            if (!file.value) return;
            // 计算文件的校验和
            const originalChecksum = await calculateChecksum(file.value);
            console.log(`原始文件校验和: ${originalChecksum}`);

            // 查询已上传的分片
            try {
                const response = await axios.get('http://localhost:3000/xxk_ce/uploaded-chunks', {
                    params: {
                        fileName: encodeURIComponent(file.value.name)// 对文件名进行编码
                    }// 发送文件名作为查询参数
                });
                uploadedChunks.value = response.data;// 将已上传的分片列表赋值给uploadedChunks
            } catch (error) {
                console.error('查询已上传分片失败:', error);
                return;
            }
            // 创建分片
            const chunks = createFileChunk(file.value);
            for (let i = 0; i < chunks.length; i++) {
                if (isChunkUploaded(i)) {
                    console.log(`分片 ${i} 已上传，跳过`);
                    continue;
                }
                try {
                    await axios.post('http://localhost:3000/xxk_ce/upload', chunks[i], {
                        onUploadProgress: (e) => {
                            // 计算当前上传进度
                            progress.value = ((i + e.loaded / e.total) / chunks.length) * 100;
                        },
                        headers: {
                            'Content-Type': 'application/octet-stream',// 设置请求头为二进制流
                            'File-Name': encodeURIComponent(file.value.name), // 对文件名进行编码
                            'File-Index': i.toString(),// 分片索引
                            'Total-Chunks': chunks.length.toString()// 分片总数
                        }
                    });
                    markChunkAsUploaded(i); // 标记分片为已上传
                } catch (error) {
                    console.error('上传失败:', error);
                    break;
                }
            }

            // 所有分片上传完成后，通知服务器合并文件
            if (progress.value === 100) {
                try {
                    await axios.post('http://localhost:3000/xxk_ce/merge', {
                        fileName: encodeURIComponent(file.value.name), // 对文件名进行编码
                        totalChunks: chunks.length,// 分片总数
                        originalChecksum: originalChecksum // 发送原始文件校验和
                    });
                    console.log('文件合并完成');
                } catch (error) {
                    console.log(file.value.name, '文件分片');
                    console.error('文件合并失败:', error);
                }
            }
        };

        return {
            file,
            progress,
            handleFileChange,
            upload
        };
    }
});
</script>